package com.sojson.util.encrypt.bothway.imparity.sm2;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.google.common.base.Preconditions;
import com.sojson.util.Util;

/**
 * 
 * 
 * @author liu
 * @date 2022-06-09
 */
public interface IKeyPairGenerator {

    String ALGORITHM = "SM2";
    String ECDSA = "ECDSA";
    String WITH = "with";
    String EC = "EC";


    /**
     * 公钥转换为 {@link ECPublicKeyParameters}
     * @param key key
     * @return
     * @throws Exception
     */
    default ECPublicKeyParameters publicKeyToParams(String algorithm, String key) throws Exception {
        return publicKeyToParams(algorithm, Util.hexToByte(key));
    }

    /**
     * 公钥转换为 {@link ECPublicKeyParameters}
     * @param key key
     * @return
     * @throws Exception
     */
    default ECPublicKeyParameters publicKeyToParams(String algorithm, byte[] key) throws Exception {
        Preconditions.checkNotNull(key, "key must be not null !");
        PublicKey publicKey = generatePublicKey(algorithm, key);
        return (ECPublicKeyParameters)ECUtil.generatePublicKeyParameter(publicKey);
    }

    /**
     * 生成公钥
     * @param algorithm 算法
     * @param key       key
     * @return
     */
    default PublicKey generatePublicKey(String algorithm, byte[] key)
        throws NoSuchAlgorithmException, InvalidKeySpecException {
        Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
        Preconditions.checkNotNull(key, "key must be not null !");
        KeySpec keySpec = new X509EncodedKeySpec(key);
        algorithm = getAlgorithmAfterWith(algorithm);
        return getKeyFactory(algorithm).generatePublic(keySpec);
    }

    /**
    * 私钥转换为 {@link ECPrivateKeyParameters}
    * @param key key
    * @return
    * @throws Exception
    */
    default ECPrivateKeyParameters privateKeyToParams(String algorithm, String key) throws Exception {
        return privateKeyToParams(algorithm, Util.hexToByte(key));
    }

    /**
    * 私钥转换为 {@link ECPrivateKeyParameters}
    * @param key key
    * @return
    * @throws Exception
    */
    default ECPrivateKeyParameters privateKeyToParams(String algorithm, byte[] key) throws Exception {
        Preconditions.checkNotNull(key, "key must be not null !");
        PrivateKey privateKey = generatePrivateKey(algorithm, key);
        return (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter(privateKey);
    }

    /**
     * 生成私钥
     * @param algorithm 算法
     * @param key       key
     * @return
     */
    default PrivateKey generatePrivateKey(String algorithm, byte[] key)
        throws NoSuchAlgorithmException, InvalidKeySpecException {
        Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
        Preconditions.checkNotNull(key, "key must be not null !");
        KeySpec keySpec = new PKCS8EncodedKeySpec(key);
        algorithm = getAlgorithmAfterWith(algorithm);
        return getKeyFactory(algorithm).generatePrivate(keySpec);
    }

    /**
     * 获取用于密钥生成的算法<br>
     * 获取XXXwithXXX算法的后半部分算法，如果为ECDSA或SM2，返回算法为EC
     * @param algorithm XXXwithXXX算法
     * @return 算法
     */
    default String getAlgorithmAfterWith(String algorithm) {
        Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
        int indexOfWith = StringUtils.lastIndexOfIgnoreCase(algorithm, WITH);
        if (indexOfWith > 0) {
            algorithm = StringUtils.substring(algorithm, indexOfWith + WITH.length());
        }
        if (ECDSA.equalsIgnoreCase(algorithm) || ALGORITHM.equalsIgnoreCase(algorithm)) {
            algorithm = EC;
        }
        return algorithm;
    }

    /**
     * 获取{@link KeyFactory}
     * @param algorithm 非对称加密算法
     * @return {@link KeyFactory}
     */
    default KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException {
        final Provider provider = new BouncyCastleProvider();
        return KeyFactory.getInstance(algorithm, provider);
    }

}